﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Timers;
using System.Windows;
using MyUtils;
using XTrace.Registry;

namespace XTrace.Viewer
{
//	class SourceFileViewModel : SourcesViewModel
//	{
//		public SourceFileViewModel(string file)
//		{
//			_sources = new ObservableCollection<SourceViewModel> {new SourceViewModel(file)};
//			SelectedSource = Sources.FirstOrDefault();
//		}
//	}

	class SourcesAutoViewModel : SourcesViewModel
	{
		readonly HashSet<string> _pathToExplicitFiles = new HashSet<string>();

		readonly Timer _timer = new Timer(1);

		public override void SwitchTo(string fileName)
		{
			if (string.IsNullOrWhiteSpace(fileName))
			{
				return;
			}
			if (!Path.IsPathRooted(fileName))
			{
				return;
			}
			if (!File.Exists(fileName))
			{
				return;
			}

			var existing = Find(fileName);

			if (existing == null)
			{
				AddExplicit(fileName);
				Sync();
				existing = Find(fileName);
				existing.EnsureExists();
			}

			SelectedSource = existing;
		}

		SourceViewModel Find(string fileName)
		{
			return Sources.FirstOrDefault(x => string.Equals(x.FullPath, fileName, StringComparison.InvariantCultureIgnoreCase));
		}

		public SourcesAutoViewModel(string pathToExplicitFiles = null)
		{
			if (pathToExplicitFiles != null)
			{
				AddExplicit(pathToExplicitFiles);
			}
			_sources = SortableObservableCollection.New<SourceViewModel>((a, b) =>
				{
					if (!a.FileExists)
					{
						return 0;
					}
					if (!b.FileExists)
					{
						return 0;
					}
					return b.LastWriteTime.CompareTo(a.LastWriteTime);
				});
			_timer.Elapsed += TimerElapsed;
			_timer.Enabled = true;
		}

		public void AddExplicit(string pathToExplicitFiles)
		{
			_pathToExplicitFiles.Add(pathToExplicitFiles);
		}

		void TimerElapsed(object sender, ElapsedEventArgs e)
		{
			const int interval = 3000;
			if (_timer.Interval != interval)
			{
				_timer.Interval = interval;
			}
			_timer.Enabled = false;
			try
			{
				Sync();
			}
			finally
			{
				_timer.Enabled = true;
			}
		}

		private void Sync()
		{
			using (P.S("Sync"))
			{
				string[] files;
				using (P.S("Sync.FindExisting"))
				{
					files = _pathToExplicitFiles.Union(SourcesProvider.Files, StringComparer.OrdinalIgnoreCase).Where(File.Exists).ToArray();
				}
				SourceViewModel[] srcs;
				lock(Sources)
				{
					srcs = Sources.ToArray();
				}
				var sourcesCopy = srcs;
				var sourcesToRemove = new List<SourceViewModel>();
				var sourcesToAdd = new List<SourceViewModel>();

				using (P.S("Sync.PrepareListForRemoving"))
				{
					foreach (var source in sourcesCopy)
					{
						if (!files.Contains(source.FullPath, StringComparer.InvariantCultureIgnoreCase))
						{
							sourcesToRemove.Add(source);
						}
					}
				}

				using (P.S("Sync.PrepareListForAdding"))
				{
					foreach (var file in files)
					{
						if (!sourcesCopy.Any(x => string.Equals(x.FullPath, file, StringComparison.InvariantCultureIgnoreCase)))
						{
							sourcesToAdd.Add(new SourceViewModel(file));
						}
					}
				}

				using (P.S("Sync.InvokeInUI"))
				{
					var app = Application.Current;
					if (app != null)
					{
						var op = app.Dispatcher.BeginInvoke((Action<List<SourceViewModel>, List<SourceViewModel>>)UpdateInUI,
							sourcesToRemove, sourcesToAdd);
						using (P.S("Sync.InvokeInUI.Wait"))
						{
							op.Wait();
						}
					}
				}
			}
		}

		void UpdateInUI(IEnumerable<SourceViewModel> sourcesToRemove, IEnumerable<SourceViewModel> sourcesToAdd)
		{
			lock (Sources)
			{
				using (P.S("UpdateInUI"))
				{
					using (P.S("UpdateInUI.Remove"))
					{
						foreach (var sourceViewModel in sourcesToRemove)
						{
							Sources.Remove(sourceViewModel);
						}

					}
					using (P.S("UpdateInUI.Add"))
					{
						foreach (var sourceViewModel in sourcesToAdd)
						{
							if (sourceViewModel == null)
							{
								XTrace.Warning("Source View Model null");
							}
							else
							{
								if (!Sources.Contains(sourceViewModel))
								{
									if (!Sources.Any(x => x.FullPath == sourceViewModel.FullPath))
									{
										Sources.Add(sourceViewModel);
									}
									else
									{
										XTrace.Warning("Source Path Already Added", sourceViewModel.FullPath);
									}
								}
								else
								{
									XTrace.Warning("Source Model Already Added", sourceViewModel.FullPath);
								}
							}
						}
					}
					using (P.S("UpdateInUI.SelectedSource"))
					{
						if (SelectedSource == null)
						{
							if (_pathToExplicitFiles.Any())
							{
								var select = _pathToExplicitFiles.First();
								SelectedSource = Sources.FirstOrDefault(x => x.FullPath == select);
							}
							else
							{
								SelectedSource = Sources.FirstOrDefault();
							}
						}
					}
				}
			}
		}
	}

	abstract class SourcesViewModel : ObservableObject
	{
		public abstract void SwitchTo(string fileName);

		protected ObservableCollection<SourceViewModel> _sources;
		public ObservableCollection<SourceViewModel> Sources
		{
			get { return _sources; }
		}

		SourceViewModel _selectedSource;
		public SourceViewModel SelectedSource
		{
			get { return _selectedSource; }
			set
			{
				using (P.S("set_SelectedSource"))
				{
					_selectedSource = value;
					OnPropertyChanged("SelectedSource");
				}
			}
		}

		InfoSize _sizeLimit = "5 MB";
		public InfoSize SizeLimit
		{
			get { return _sizeLimit; }
			set
			{
				if (_sizeLimit != value)
				{
					_sizeLimit = value;
					OnPropertyChanged("SizeLimit");
				}
			}
		}

		XTraceLevel _level = XTraceLevel.All;
		public XTraceLevel Level
		{
			get { return _level; }
			set
			{
				_level = value;
				OnPropertyChanged("Level");
			}
		}

		public IEnumerable<XTraceLevel> Levels
		{
			get { return (XTraceLevel[])Enum.GetValues(typeof (XTraceLevel)); }
		}


	}

}
